package com.yanfan.xmzS7;

import cn.hutool.core.thread.ThreadUtil;
import com.github.s7connector.api.DaveArea;
import com.github.s7connector.api.S7Connector;
import com.github.s7connector.api.S7Serializer;
import com.github.s7connector.api.factory.S7ConnectorFactory;
import com.github.s7connector.exception.S7Exception;
import com.github.s7connector.impl.serializer.S7SerializerImpl;
import com.github.s7connector.impl.serializer.converter.IntegerConverter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalTime;
import java.util.regex.Pattern;

@Slf4j
@Getter
public class S7ReconnectConnector {

    private S7Connector connector;
    private S7Serializer serializer;
    private String ip;
    private final int PLC_RACK = 0;              // 机架号
    private final int PLC_SLOT = 1;               // 插槽号
    private final int DB_NUM;              // 机架号
    private final int BYTE_OFFSET;               // 偏移量
    private volatile boolean isConnected = false;

    public static final int DEFAULT_DB_NUM = 1;
    public static final int DEFAULT_BYTE_OFFSET = 0;
    public static final int DEFAULT_HEARTBEAT_OFFSET = 114;

    public S7ReconnectConnector(String ip) {
        this(ip, DEFAULT_DB_NUM, DEFAULT_BYTE_OFFSET);
    }

    public S7ReconnectConnector(String ip, int dbNum, int byteOffset) {
        this.ip = ip;
        this.DB_NUM = dbNum;
        this.BYTE_OFFSET = byteOffset;
        initializeConnection(ip);
    }

    // 初始化连接（含重试逻辑）
    public boolean initializeConnection() {
        return this.initializeConnection(this.ip);
    }

    public boolean initializeConnection(String ip) {
        this.ip = ip;
        // 验证IP是否正确
        if (!isValidIP(this.ip)) {
            log.warn("=> S7 PLC 建立连接失败，IP格式异常，忽略链接 ip：{} ", ip);
            return false;
        }

        closeConnection(); // 清理旧连接
        isConnected = false;
        try {
            connector = S7ConnectorFactory
                    .buildTCPConnector()
                    .withHost(ip)
                    .withRack(PLC_RACK)
                    .withSlot(PLC_SLOT)
                    .withTimeout(5000) // 设置超时
                    .build();

            serializer = new S7SerializerImpl(connector);
            isConnected = true;
        } catch (S7Exception e) {
            log.error("连接失败 initializeConnection ip：{} ，msg: {}", ip, e.getMessage());
        }
        return isConnected;
    }

    // 数据写入
    public void writeData(Object entity) {
        this.writeData(entity, DB_NUM, BYTE_OFFSET);
    }

    // 数据写入
    public void writeData(Object entity, int dbNumber, int byteOffset) {
        serializer.store(entity, dbNumber, byteOffset);
    }

    // 数据读取（含自动重连）
    public <T> T readData(Class<T> targetClass) {
        return readData(targetClass, DB_NUM, BYTE_OFFSET);
    }

    // 数据读取（含自动重连）
    /*public boolean heartbeat() {
        IntegerConverter converter = new IntegerConverter();
        int byteOffset = 0;
        int bitOffset = 0;
        // 从PLC读取数据 (DB1 - 数据块2，字地址2)
        byte[] bytes = connector.read(DaveArea.DB, DB_NUM, 2, 2);
        Integer data = converter.extract(Integer.class, bytes, byteOffset, bitOffset);
        return data > 0;
    }*/
    // 数据读取（含自动重连）
    public boolean heartbeat() {
        try {
            IntegerConverter converter = new IntegerConverter();
            LocalTime now = LocalTime.now();
            int second = now.getSecond();
            // 当前时间的 时分秒 int 6位数
            byte[] wbytes = new byte[2];
            int byteOffset = 0;
            int bitOffset = 0;
            converter.insert(second, wbytes, byteOffset, bitOffset, 2);
            connector.write(DaveArea.DB, DB_NUM, DEFAULT_HEARTBEAT_OFFSET, wbytes);
            // 从PLC读取数据
            byte[] bytes = connector.read(DaveArea.DB, DB_NUM, 2, DEFAULT_HEARTBEAT_OFFSET);
            Integer data = converter.extract(Integer.class, bytes, byteOffset, bitOffset);
            return data == second;
        } catch (Exception e) {
            return false;
        }
    }

    // 数据读取（含自动重连）
    public <T> T readData(Class<T> targetClass, int dbNumber, int byteOffset) {
        return serializer.dispense(targetClass, dbNumber, byteOffset);
    }


    // 数据读取（含自动重连）
    public <T> T readData(Class<T> targetClass, int dbNumber, int byteOffset, int blockSize) {
        return serializer.dispense(targetClass, dbNumber, byteOffset, blockSize);
    }

    // 处理断开连接
    public void handleDisconnect(Exception ex) {
        log.error("连接异常 handleDisconnect: {}", ex.getMessage());
        closeConnection(); // 清理旧连接

        // 指数退避重连策略
        int baseDelay = 1000;
        int maxDelay = 60000;
        int attempts = 0;

        while (!isConnected) {
            attempts++;
            int delay = Math.min((int) (baseDelay * Math.pow(2, attempts)), maxDelay);
            try {
                System.out.println("尝试重连 handleDisconnect (" + attempts + ")...");
                isConnected = initializeConnection();

                if (isConnected) {
                    System.out.println("重连成功！handleDisconnect ");
                    return; // 重连成功退出循环
                }
            } catch (Exception e) {
                log.error("重连失败 handleDisconnect: {}", e.getMessage());
                ThreadUtil.sleep(delay);
            }
        }
    }

    // 安全关闭连接
    private void closeConnection() {
        try {
            if (connector != null) {
                connector.close();
            }
            isConnected = false;
        } catch (Exception e) {
            log.error("关闭连接异常: {}", e.getMessage());
        }
    }


    public void shutdown() {
        closeConnection();
    }



    // IPv4正则表达式
    private static final String IPV4_PATTERN =
            "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";

    // IPv6正则表达式（简化版）
    private static final String IPV6_PATTERN =
            "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$";

    private static final Pattern IPv4_PATTERN_REGEX = Pattern.compile(IPV4_PATTERN);
    private static final Pattern IPv6_PATTERN_REGEX = Pattern.compile(IPV6_PATTERN);

    /**
     * 检查字符串是否为有效的IPv4地址
     * @param ip 要检查的字符串
     * @return 如果是有效的IPv4地址返回true，否则返回false
     */
    public static boolean isValidIPv4(String ip) {
        if (ip == null || ip.isEmpty()) {
            return false;
        }
        return IPv4_PATTERN_REGEX.matcher(ip).matches();
    }

    /**
     * 检查字符串是否为有效的IPv6地址
     * @param ip 要检查的字符串
     * @return 如果是有效的IPv6地址返回true，否则返回false
     */
    public static boolean isValidIPv6(String ip) {
        if (ip == null || ip.isEmpty()) {
            return false;
        }
        return IPv6_PATTERN_REGEX.matcher(ip).matches();
    }

    /**
     * 检查字符串是否为有效的IP地址（IPv4或IPv6）
     * @param ip 要检查的字符串
     * @return 如果是有效的IP地址返回true，否则返回false
     */
    public static boolean isValidIP(String ip) {
        return isValidIPv4(ip) || isValidIPv6(ip);
    }


    public static void main(String[] args) {
        S7ReconnectConnector connector = new S7ReconnectConnector("127.0.0.1", 1, 0);
        connector.writeData(StPlcS7Entity.builder().judgmentResults(0).waterBubblesLeaking(0.0).heartBeat(1).stop(false).wstop(false).build());
//        StPlcS7Entity entity = connector.readData(StPlcS7Entity.class);
//        System.out.println(entity);
//        StPlcS7Entity entity1 = connector.readData(StPlcS7Entity.class);
//        System.out.println(entity1);
        boolean heartbeat = connector.heartbeat();
        System.out.println(heartbeat);
    }
}
